//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//
/*++

THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name: 

        usb11fn.h

Abstract:

        PXA USB Function Driver Header.

--*/
#ifndef __PXA_USB11FN_H_
#define __PXA_USB11FN_H_
#include <monahans_usbd.h>
#include <monahans_clkmgr.h>
#include <csync.h>
#include <cmthread.h>
#include <CRegEdit.h>
#include <CRefCon.h>
#include <usbfn.h>

#include <xllp_dmac.h>

#ifndef SHIP_BUILD
#define STR_MODULE _T("pxa_usbfn!")
#define SETFNAME() LPCTSTR pszFname = STR_MODULE _T(__FUNCTION__) _T(":")
#else
#define SETFNAME()
#endif

#define MAX_ENDPOINT_NUMBER 0x18

#define MAX_PKT_SIZE      4096L
#define NUM_DESC         16
#define PADDING_SIZE    96
#define MAX_TRS_SIZE    64
#if PADDING_SIZE>=MAX_PKT_SIZE
#error MAX_PKT_SIZE should great than PADDING_SIZE
#endif


#ifdef DEBUG
#define ZONE_DMA        DEBUGZONE(11)
#endif 

class PXAUsbDevice ;
class PXAEndpoint ;

#define ENDPOINT_CONTROL_STATUS_REGISTER_OFFSET (0x100/sizeof(DWORD))
#define ENDPOINT_BYTECOUNT_REGISTER_OFFSET      (0x200/sizeof(DWORD))
#define ENDPOINT_DATA_REGISTER_OFFSET           (0x300/sizeof(DWORD))
#define ENDPOINT_CONFIGURATION_REGISTER_OFFSET  (0x400/sizeof(DWORD))
#define DEVICE_UP2OCR_REGISTER (0x20/sizeof(DWORD))

#define DEVICE_CONTROL_REGISTER (0/sizeof(DWORD))
#define DEVICE_INT_CR0_REGISTER (4/sizeof(DWORD))
#define DEVICE_INT_CR1_REGISTER (8/sizeof(DWORD))
#define DEVICE_INT_SR0_REGISTER (0xc/sizeof(DWORD))
#define DEVICE_INT_SR1_REGISTER (0x10/sizeof(DWORD))
#define DEVICE_Frame_NUMBER_REGISTER (0x14/sizeof(DWORD))

#define DEVICE_OTGICR_REGISTER (0x18/sizeof(DWORD))
#define DEVICE_OTGISR_REGISTER (0x1c/sizeof(DWORD))


// Registry Value.
#define PXA_USBFUNCTION_DOUBLEBUFFER_VALNAME TEXT("DoubleBuffer")
#define PXA_USBFUNCTION_DOUBLEBUFFER_VALTYPE REG_DWORD
#define PXA_USBFUNCTION_PRIORITY_VALNAME    TEXT("Priority256")
#define PXA_USBFUNCTION_PRIORITY_VALTYPE    REG_DWORD
// Debugging only registry.
#define PXA_USBFUNCTION_EP0_STALL_COUNTER_VALNAME TEXT("EP0StallCounter")
#define PXA_USBFUNCTION_EP0_STALL_COUNTER_VALTYPE REG_DWORD
#define PXA_USBFUNCTION_BAD_SETUP_COUNTER_VALNAME TEXT("BadSetupCounter")
#define PXA_USBFUNCTION_BAD_SETUP_COUNTER_VALTYPE REG_DWORD

#define PXA_USBFUNCTION_DEFAULT_PRIORITY    100

/// Endpoint Container 
/// Empty Class, just provide class type
class CEndpointContainer : public CStaticContainer <PXAEndpoint, MAX_ENDPOINT_NUMBER>
{
};

/// USB Device class
/// This is the main class of UDC driver
class PXAUsbDevice :public CEndpointContainer, public CRegistryEdit, public CMiniThread 
{
public:
    /// UDC Register Base Physical Address. Need such infomation to Config DMA Descriptor     
    XLLP_UINT32_T  m_physicalbase;

    /// UDC Register Virtual Address
    volatile PULONG  m_pUsbDevReg;

    /// Contruct function
    /// @param  lpActivePath    [IN] Active Key for the interface driver
    PXAUsbDevice(LPCTSTR lpActivePath);

    /// Deconstruction Function
    /// Unmap address, close handle
    virtual ~PXAUsbDevice();

    /// PDD Initialized
    /// Map IO Space, init hardware
    /// @param  pvMddContext        [IN]Mdd Context
    /// @param  pMddInterfaceInfo       [IN]MDD interface function infomation
    /// @param  pPddInterfaceInfo       [OUT] Pdd Interface info, set this point
    /// @return  ERROR_SUCCESS      Success,    
    ///          other value    failed
    virtual DWORD Init(PVOID pvMddContext,
        PUFN_MDD_INTERFACE_INFO pMddInterfaceInfo, PUFN_PDD_INTERFACE_INFO pPddInterfaceInfo);

    /// Remove all endpoint
    /// @return     TRUE    Success
    ///             FALSE   Fail    
    virtual BOOL DeleteAllEndpoint();

    /// Check Endpoint if support 
    /// Endpoint will be initialized 
    ///
    /// @param  dwEndpoint          [IN] Endpoint Number
    /// @param  Speed               [IN] Bus speed, high or Full
    /// @param  pEndpointDesc       [IN] Endpoint descriptor
    /// @param  bConfigurationValue  [IN] Configuration number
    /// @param  bInterfaceNumber        [IN] Interface Number
    /// @param  bAlternateSetting       [IN] Alternate setting number
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD IsEndpointSupportable (DWORD dwEndpoint,UFN_BUS_SPEED Speed,PUSB_ENDPOINT_DESCRIPTOR pEndpointDesc,
            BYTE bConfigurationValue=1, BYTE bInterfaceNumber=0, BYTE bAlternateSetting=0 );

    /// Initialze endpoint
    ///
    /// @param  dwEndpoint          [IN] Endpoint Number
    /// @param  Speed               [IN] Bus speed, high or Full
    /// @param  pEndpointDesc       [IN] Endpoint descriptor
    /// @param  bConfigurationValue  [IN] Configuration number
    /// @param  bInterfaceNumber        [IN] Interface Number
    /// @param  bAlternateSetting       [IN] Alternate setting number
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD InitEndpoint(DWORD dwEndpoint,UFN_BUS_SPEED Speed,PUSB_ENDPOINT_DESCRIPTOR pEndpointDesc,
            BYTE bConfigurationValue=1, BYTE bInterfaceNumber=0, BYTE bAlternateSetting=0 );


    /// Deinit Endpoint 
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD DeinitEndpoint(DWORD dwEndpoint );

    /// Send Stall at Endpoint
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD StallEndpoint(DWORD dwEndpoint );

    /// Clear Stall at Endpoint
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD ClearEndpointStall( DWORD dwEndpoint );


    /// Reset Endpoint
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD ResetEndpoint(DWORD dwEndpoint );

    /// Check Endpoint status
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @param  pfHalted            [OUT] return if endpoint halt
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD IsEndpointHalted( DWORD dwEndpoint, PBOOL pfHalted );

    /// Start a transfer at the special Endpoint
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @param  pTransfer           [IN] Data structure will be transfered
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD IssueTransfer(DWORD  dwEndpoint,PSTransfer pTransfer );


    /// Give up a transfer at the special Endpoint
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @param  pTransfer           [IN] Data structure will be transfered
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD AbortTransfer(DWORD dwEndpoint, PSTransfer pTransfer);

    /// Send Zero handshake package at endpoint 0
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @param  pTransfer           [IN] Data structure will be transfered
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    /// @remarks  Endpoint Zero Special
    virtual DWORD SendControlStatusHandshake(DWORD dwEndpoint);

//  Device Function.
    /// Start Device
    /// Initialize all opened endpoint
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD Start();

    /// Stop Device
    /// DeInitialize all opened endpoint
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD Stop();

    /// Check if usb Cable attached
    /// @return     TRUE        Attached
    ///             FALSE       Detached
    virtual BOOL IsCableAttached() { return TRUE; };

    /// Software Disconnect
    /// Remove D+/D- pull up resister
    virtual void CablePowerOn() {
        DEBUGMSG(1, (TEXT("PXAUsbDevice::CablePowerOn:\r\n")));
    };

    /// Software Connect 
    /// Pull up D+ or D-
    virtual void CablePowerOff() {
        DEBUGMSG(1, (TEXT("PXAUsbDevice::CablePowerOff:\r\n")));
    };

    /// Set Address
    /// UDC handle it automatically. Software do nothing
    /// @return     ERROR_SUCCESS           Success
    virtual DWORD  SetAddress( BYTE  bAddress );

    /// Initiate RemoteWakeup
    /// force the UDC out of suspend
    /// @return     ERROR_SUCCESS           Success
    virtual void  Initiate_RemoteWakeup();
    virtual DWORD IsConfigurationSupportable(UFN_BUS_SPEED Speed,PUFN_CONFIGURATION pConfiguration);

    ///Power Down which called when system suspend. 
    virtual void PowerDown();

    ///Power Up which called when system resume. 
    virtual void PowerUp() ;

    ///Set Power State
    /// @param cpsNew   [IN] new power state
    virtual void  SetPowerState( CEDEVICE_POWER_STATE cpsNew ) ;

    /// IO Control
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    /// @remarks IOCTL_UFN_GET_PDD_INFO tell MDD, PDD is able to support DMA. 
    virtual DWORD IOControl( IOCTL_SOURCE source, DWORD dwCode, PBYTE  pbInBuf, DWORD cbInBuf, PBYTE pbOutBuf, DWORD cbOutBuf,PDWORD  pcbActualOutBuf );

    /// OTG Setup feature handle
    void    OTGSetupFeature() {
        m_fOTGSetupFeature = TRUE;
        ISTProcess();
    }
//  Register Access.

    /// Write UDC Register
    /// @param  dwOffset        [IN] offset of UDC register
    /// @param  dwData          [IN] Data will be Written to register   
    void WriteUDCRegister(DWORD dwOffset, DWORD dwData) { 
        PREFAST_ASSERT(m_pUsbDevReg!=NULL);
        WRITE_REGISTER_ULONG(m_pUsbDevReg + dwOffset, dwData);
    }


    /// Read UDC Register
    /// @param  dwOffset        [IN] offset of UDC register
    /// @return     value of register
    DWORD ReadUDCRegister(DWORD dwOffset) {
        PREFAST_ASSERT(m_pUsbDevReg!=NULL);
        return READ_REGISTER_ULONG(m_pUsbDevReg + dwOffset);
    }

    /// Write a byte UDC Register
    /// @param  dwOffset        [IN] offset of UDC register
    /// @param  dwData          [IN] Data will be Written to register   
    void WriteUDCRegisterByte(DWORD dwOffset, BYTE bData) {
        PREFAST_ASSERT(m_pUsbDevReg!=NULL);
        WRITE_REGISTER_UCHAR((PUCHAR)(m_pUsbDevReg + dwOffset),bData);
    }

    /// Read a byte UDC Register
    /// @param  dwOffset        [IN] offset of UDC register
    /// @return     value of register
    BYTE ReadUDCRegisterByte(DWORD dwOffset) {
        PREFAST_ASSERT(m_pUsbDevReg!=NULL);
        return READ_REGISTER_UCHAR ((PUCHAR)(m_pUsbDevReg + dwOffset));
    }

// Device Register Access.
    /// Read UDCCR
    /// @return     value of register
    DWORD   ReadControlRegister() {  return ReadUDCRegister(DEVICE_CONTROL_REGISTER); }

    /// Write UDCCR Register
    /// @param  dwData      [IN]Data will be Written to register
    void    WriteControlRegister(DWORD dwData) { WriteUDCRegister(DEVICE_CONTROL_REGISTER, dwData);}

    /// Read UDCICR0
    /// @return     value of register
    DWORD   ReadIntrCtr0Register() { return ReadUDCRegister(DEVICE_INT_CR0_REGISTER); }

    /// Read UDCICR1
    /// @return     value of register
    DWORD   ReadIntrCtr1Register() { return ReadUDCRegister(DEVICE_INT_CR1_REGISTER); }

    /// Read UDCISR0
    /// @return     value of register
    DWORD   ReadIntrStatus0Register() { return ReadUDCRegister(DEVICE_INT_SR0_REGISTER); }

    /// Read UDCISR1
    /// @return     value of register
    DWORD   ReadIntrStatus1Register() { return ReadUDCRegister(DEVICE_INT_SR1_REGISTER); }

    /// Write UDCICR0 Register
    /// @param  dwData      [IN]Data will be Written to register
    void    WriteIntrCtr0Register(DWORD dwData) { WriteUDCRegister(DEVICE_INT_CR0_REGISTER, dwData); }

    /// Write UDCICR1 Register
    /// @param  dwData      [IN]Data will be Written to register
    void    WriteIntrCtr1Register(DWORD dwData) { WriteUDCRegister(DEVICE_INT_CR1_REGISTER, dwData); }

    /// Write UDCISR0 Register
    /// @param  dwData      [IN]Data will be Written to register        
    void    WriteIntrStatus0Register(DWORD dwData) { WriteUDCRegister(DEVICE_INT_SR0_REGISTER,dwData); }

    /// Write UDCISR1 Register
    /// @param  dwData      [IN]Data will be Written to register    
    void    WriteIntrStatus1Register(DWORD dwData) { WriteUDCRegister(DEVICE_INT_SR1_REGISTER,dwData); }

    /// Read UDCOTGICR
    /// @return     value of register
    DWORD   ReadOTGICR() { return ReadUDCRegister(DEVICE_OTGICR_REGISTER); };

    /// Write UDCOTGICR Register
    /// @param  dwData      [IN]Data will be Written to register    
    void    WriteOTGICR(DWORD dwData) { WriteUDCRegister(DEVICE_OTGICR_REGISTER,dwData); }

    /// Read UDCOTGISR
    /// @return     value of register
    DWORD   ReadOTGISR() { return ReadUDCRegister(DEVICE_OTGISR_REGISTER); };

    /// Write UDCOTGISR Register
    /// @param  dwData      [IN]Data will be Written to register    
    void    WriteOTGISR(DWORD dwData) { WriteUDCRegister(DEVICE_OTGISR_REGISTER,dwData); };
    
    /// Interrupt
    /// Enable/Disable Endpoint Interrupt
    /// @param dwEndpointIndex      [IN] Endpoint Number
    /// @param bEnable              [IN] TRUE enable, FALSE disable
    /// @return     TRUE    Sucess
    ///             FALSE   Fail
    BOOL    EnableEndpointInterrupt(DWORD dwEndpointIndex,BOOL bEnable);


    /// Get Endpoint interrupt status
    /// @param dwEndpointIndex      [IN] Endpoint Number
    /// @return     value of register
    DWORD   GetEndpointIntrStatus(DWORD dwEndpointIndex);


    /// Call MDD notify function to tell MDD transfer complete
    /// @param  pTransfer  [IN] Data transfer structure
    void MddTransferComplete(PSTransfer pTransfer) {
        SETFNAME();
        if (m_pvMddContext && pTransfer) {
            DEBUGMSG(ZONE_FUNCTION, (_T("%s MddTransferComplete pTransfer:0x%x"),pszFname,pTransfer));
            m_pfnNotify(m_pvMddContext, UFN_MSG_TRANSFER_COMPLETE, (DWORD) pTransfer);
        }
    }

    /// Send Device Nofity message to MDD
    /// @param      dwMsg       [IN] Message type
    /// @param      dwParam     [IN] Message param 
    /// @return     TRUE    Success
    ///             FALSE   Fail
    BOOL DeviceNotification( DWORD dwMsg, DWORD dwParam ) {
        SETFNAME();
        if (m_pvMddContext) {
            DEBUGMSG(ZONE_FUNCTION, (_T("%s DeviceNotification dwMsg:0x%x,dwParam:0x%x"),pszFname,dwMsg,dwParam));
            return m_pfnNotify(m_pvMddContext, dwMsg, dwParam);
        }
        else {
            DebugBreak();
            return FALSE;
        }
    }

    ///Get DMA Handle
    /// @return DMA handle
    inline P_XLLP_DMAC_T GetXllpDMAHandle(){return m_XllpDMAHandle;}
public:
    /// Inc Ep0 Stall coundter
    /// @remarks Debugging function.
    void IncEp0StallCounter() {
#ifdef ERRORCOUNTERINREGISTRY
        DWORD dwEp0StallCounter = 0;
        if (!GetRegValue(PXA_USBFUNCTION_EP0_STALL_COUNTER_VALNAME,(LPBYTE) &dwEp0StallCounter,sizeof(dwEp0StallCounter))) {
            dwEp0StallCounter = 0;
        }
        dwEp0StallCounter++;
        RegSetValueEx( PXA_USBFUNCTION_EP0_STALL_COUNTER_VALNAME,PXA_USBFUNCTION_EP0_STALL_COUNTER_VALTYPE,
            (PBYTE)&dwEp0StallCounter,sizeof(dwEp0StallCounter));
#endif
    }

    /// Inc Ep0 Bad Setup coundter
    /// @remarks Debugging function.
    void IncBadSetupCounter() {
#ifdef ERRORCOUNTERINREGISTRY
        DWORD dwBadSetupCounter = 0;
        if (!GetRegValue(PXA_USBFUNCTION_BAD_SETUP_COUNTER_VALNAME,(LPBYTE) &dwBadSetupCounter,sizeof(dwBadSetupCounter))) {
            dwBadSetupCounter = 0;
        }
        dwBadSetupCounter++;
        RegSetValueEx( PXA_USBFUNCTION_BAD_SETUP_COUNTER_VALNAME, PXA_USBFUNCTION_BAD_SETUP_COUNTER_VALTYPE,
            (PBYTE)&dwBadSetupCounter,sizeof(dwBadSetupCounter));
#endif
    }
protected: 
    BOOL  m_fResumeOccurred;            ///< If Resume happen
    BOOL  m_fForceResume;               ///< force enumeration when resume

    //virtual PXAEndpoint * GetEndpointBy(DWORD dwEndpointIndex) {        
    //    return  (dwEndpointIndex<MAX_ENDPOINT_NUMBER ? m_EndpointArray[dwEndpointIndex]:NULL);
    //}

    /// Power Manage Function
    /// @param boff     TRUE: suspend  FALSE: resume
    virtual void PowerMgr(BOOL bOff);

        
    UFN_PDD_INFO m_PDDInfo; ///< PDD Info, tell MDD support DMA
    
    volatile PPXA_CLKMGR_REG m_pDCCLKReg;      ///< Clock Manage base address
    volatile int *  m_pBPMUReg;                 ///< BPMU Reg base address  
    BOOL    m_fIsCableAttached;                 ///< current cable statue
    BOOL    m_fIsSuspended;                 ///< current cable statue
    
    //PXAEndpoint *   m_EndpointArray[MAX_ENDPOINT_NUMBER];

    // IST
    DWORD       m_dwSysIntr;                        ///< System Interrupt Number
    HANDLE      m_hISTEvent;                        ///< IST event
    DWORD       m_dwPriority;                       ///< IST thread priority

    BOOL        m_fDoubleBuffer;                    ///< Does support double buffer
    PVOID       m_pvMddContext;                     ///< MDD Context

// Protected Fucntion
    /// Hardware Init
    /// Clear all interrupt status. enable UDE
    /// @return     TRUE    Success
    ///             FALSE   Fail
    BOOL        HardwareInit();


    /// Reinit all endpoint
    /// @return     TRUE    Success
    ///             FALSE   Fail
    /// @remarks    
    ///         Cable Detach & Attach , We have to re-init the Device Controller.
    BOOL        ReInit(); 
    // Device Info.
    DWORD       m_dwCurConfigure;   ///< Current Configure
    DWORD       m_dwCurInterface;   ///< Current Interface number

    PFN_UFN_MDD_NOTIFY      m_pfnNotify;        ///<MDD event nofity call back function
    CEDEVICE_POWER_STATE    m_CurPowerState;        ///< Current Power State
    HANDLE                  m_hParent;          ///< Parant bus handle

    P_XLLP_DMAC_T m_XllpDMAHandle;  ///<XLLP DMA Handler
    HANDLE  m_hOTGEvent;            ///< OTG Event handler
    void    ISTProcess();               ///< IST process
    BOOL    m_fOTGSetupFeature;     ///<does OTG Setup feature
    BOOL    m_bTermOtgThread;
    static DWORD WINAPI OTGThread( LPVOID dParam); ///< Thread for handling OTG and cable event

private:        
    DWORD ThreadRun();              ///< IST Thread
    UDCCR   m_prevUDCR;             ///< prev UDCR value, to OTG feature Support
};

/// Endpoint base Class
/// Handle All endpoint related operation
class PXAEndpoint: public CRefObject,public CLockObject, public CMiniThread 
{
public :
    /// Contruct function
    /// Init member varible, allocate DMA buffer if DMA enable
    /// @param pUsbDevice       [IN]    UDC Devive object
    /// @param dwPipeIndex      [IN] Pipe Number, equal with Endpoint number
    /// @param bDoubleBuffer        [IN] Enable/Disable Double buffer
    /// @param bDMAEnable       [IN] Enable/Disable DMA support
    PXAEndpoint(PXAUsbDevice * const pUsbDevice, const DWORD dwPipeIndex,BOOL bDoubleBuffer = TRUE, BOOL bDMAEnable = FALSE );

    /// Deconstruction Function
    /// upmap address, close handle, free DMA memory
    virtual ~PXAEndpoint();

    /// Init Endpoint
    /// @param  pEndpointDesc       [IN] Endpoint descriptor
    /// @param  bConfigurationValue  [IN] Configuration number
    /// @param  bInterfaceNumber        [IN] Interface Number
    /// @param  bAlternateSetting       [IN] Alternate setting number
    /// @return      TRUE           Success
    ///              FALSE          Fail
    virtual BOOL Init(PUSB_ENDPOINT_DESCRIPTOR pEndpointDesc,
            BYTE bConfigurationValue, BYTE bInterfaceNumber, BYTE bAlternateSetting);

    /// Reinit Endpoint 
    virtual BOOL ReInit();
    
    /// Get DMA Enable status
    /// @return     TRUE        DMA Enabled
    ///             FALSE           DMA Disabled
    inline BOOL GetDMAEnable(){return m_bDMAEnable;}

    /// Read Endpoint Control Status Register
    /// @return value of endpoint control status register
    DWORD   ReadControlStatusRegister() {
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        return m_pUsbDevice->ReadUDCRegister( ENDPOINT_CONTROL_STATUS_REGISTER_OFFSET + m_dwEndpointIndex);
    }; 

    /// Write Endpoint Control Status Register
    /// @param  dwData  [IN]value of endpoint control status register
    void    WriteControlStatusRegister(DWORD dwData) {
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        m_pUsbDevice->WriteUDCRegister( ENDPOINT_CONTROL_STATUS_REGISTER_OFFSET + m_dwEndpointIndex,dwData);
    };

    
    /// Read Byte Counter Register
    /// @return  value of byte counter register
    DWORD   ReadByteCountRegister(){
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        return ((m_pUsbDevice->ReadUDCRegister( ENDPOINT_BYTECOUNT_REGISTER_OFFSET + m_dwEndpointIndex)) & 0x3ff);
    }

    
    /// Read Byte from FIFO
    /// @return     value in FIFO
    BYTE    ReadDataRegisterByte() {
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        return (m_pUsbDevice->ReadUDCRegisterByte( ENDPOINT_DATA_REGISTER_OFFSET + m_dwEndpointIndex));
    }
    
    /// Read DWORD from FIFO.
    /// @return value in FIFO 
    DWORD    ReadDataRegister() {
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        return ( m_pUsbDevice->ReadUDCRegister( ENDPOINT_DATA_REGISTER_OFFSET + m_dwEndpointIndex));
    }

    /// Write Byte to FIFO
    /// @param  bData   [IN] data will be written to FIFO
    void    WriteDataRegisterByte(BYTE bData) {
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        m_pUsbDevice->WriteUDCRegisterByte( ENDPOINT_DATA_REGISTER_OFFSET + m_dwEndpointIndex, bData);
    }
    void    WriteDataRegister(DWORD dwData) {
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        m_pUsbDevice->WriteUDCRegister( ENDPOINT_DATA_REGISTER_OFFSET + m_dwEndpointIndex, dwData);
    }
    
    /// Read Endpoint Configuration Register
    /// @return value in Configuration 
    DWORD   ReadConfigurationRegister() {
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        if (m_pUsbDevice!=0) { // Endpoint ZERO is not supported
            return m_pUsbDevice->ReadUDCRegister( ENDPOINT_CONFIGURATION_REGISTER_OFFSET + m_dwEndpointIndex );
        }
        else {
            ASSERT(FALSE);
            return 0;
        }
    }

    /// Write Endpoint Configuration Register
    /// @param  dwData      [IN] Data will be written to register
    void    WriteConfigurationRegister(DWORD dwData) {
        PREFAST_ASSERT(m_pUsbDevice!=NULL);
        PREFAST_ASSERT(m_dwEndpointIndex<MAX_ENDPOINT_NUMBER);
        if (m_pUsbDevice!=0) { // Endpoint ZERO is not supported
            m_pUsbDevice->WriteUDCRegister( ENDPOINT_CONFIGURATION_REGISTER_OFFSET + m_dwEndpointIndex, dwData );
        }
        else {
            ASSERT(FALSE);
        }
    }

    
    /// Initialze endpoint
    ///
    /// @param  Speed               [IN] Bus speed, high or Full
    /// @param  pEndpointDesc       [IN] Endpoint descriptor
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    /// @remarks  Endpoint initialized in funcation Init(). This function just enables interrupt
    ///
    virtual DWORD InitEndpoint(UFN_BUS_SPEED Speed,PUSB_ENDPOINT_DESCRIPTOR pEndpointDesc) {
        // We should Reset Data Toggle Bit to DATA0. But we do know how.
        return (m_pUsbDevice->EnableEndpointInterrupt(m_dwEndpointIndex,TRUE)?ERROR_SUCCESS:ERROR_GEN_FAILURE);
    }

    /// DeInitialze endpoint
    ///
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    ///
    /// @remarks  Endpoint Deinitialized in funcation Init(). This function just disable interrupt
    ///
    virtual DWORD DeinitEndpoint() {
        return (m_pUsbDevice->EnableEndpointInterrupt(m_dwEndpointIndex,FALSE)?ERROR_SUCCESS:ERROR_GEN_FAILURE);
    }

    /// Stall endpoint
    ///
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD StallEndpoint();

    /// Clear endpoint 
    /// 
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD ClearEndpointStall();


    /// Reset endpoint 
    /// Flush FIFO, clear all status bit
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    virtual DWORD ResetEndpoint();

    /// Is Endpoint halted
    /// @return         endpoint halt status
    virtual DWORD IsEndpointHalted(PBOOL pfHalted );

    /// Start transfer
    /// @param  pTransfer       [IN]Data structure is transfered
    /// @remarks    This is important functions. Start Dma if dma enabled. 
    virtual DWORD IssueTransfer(PSTransfer pTransfer ) ;

    /// Give up transfer
    /// Stop DMA if DMA enable, Clear current pTransfer. Tell MDD transfer finished with error
    /// @param  pTransfer       [IN]Data structure is transfered
    virtual DWORD AbortTransfer(PSTransfer pTransfer );

    /// Send Zero handshake package at endpoint 0
    /// @param  dwEndpoint      [IN] Endpoint Number
    /// @return     ERROR_SUCCESS           Success
    ///             Other Value             Fail
    /// @remarks  Endpoint Zero Special
    virtual DWORD SendControlStatusHandshake() { return ERROR_INVALID_PARAMETER; };

    /// Get Endpoint Descriptor
    /// @return     Current Descriptor 
    USB_ENDPOINT_DESCRIPTOR GetEndpointDescriptor() { return m_epDesc; };

    /// IST 
    /// @return never return until driver upload 
    virtual DWORD   IST(DWORD dwIRBit) = 0 ;

    /// Start DMA Transfer
    virtual void StartDMA(){};

#ifdef DEBUG
    /// Dump DMA Descriptor
    void DumpDescript();
#endif

protected:

    PXAUsbDevice * const    m_pUsbDevice;           ///< Point to USB Device
    const DWORD             m_dwEndpointIndex;      ///< Endpoint index
    USB_ENDPOINT_DESCRIPTOR m_epDesc;               ///< Endpoint descriptor

    BYTE            m_bConfigurationValue,m_bInterfaceNumber,m_bAlternateSetting;
    
    volatile ULONG *        m_dataport;             ///< FIFO Virtual Address
    BOOL            m_fDoubleBuffer;                ///< Double buffer enable/disable
    PSTransfer      m_pCurTransfer;                 ///< Current Transfer
    BOOL            m_fZeroPacket;                  ///< is send zero package
    BOOL            m_fStalled;                     ///< is stall endpoint
    XLLP_UINT32_T       m_phydataport;              ///< Physical address for DATA port of the USB endpoint

    // DMA support  
    int             m_Magic;                        ///< DMA structure Magic number

    XLLP_DMAC_CHANNEL_T m_DmaChannel;               ///< DMA Channel
    
    volatile XLLP_UINT8_T *     m_dmadata;          ///< DMA data buffer virtual pointer
    XLLP_UINT32_T       m_phydmadata;               ///< DMA data buffer physical pointer
  
    DMA_ADAPTER_OBJECT m_DmaAdapter;                ///< DMA adaptor object, required by XLLP_DMAC module

    
    volatile XLLP_DMAC_DESCRIPTOR_T * m_desc;       ///< DMA descriptor array, virtual address
    XLLP_UINT32_T       m_phydesc;                  ///< DMA descriptor array, physical address
    
    XLLP_UINT32_T       m_dmaoff;                   ///< Current data offset
    XLLP_UINT32_T       m_phydmaoff;                ///< Physical address of current DMA data pointer.

    PHYSICAL_ADDRESS        m_PA;                   ///< Phycial address
    XLLP_BOOL_T         m_DMATransfering;           ///< is DMA transferring

    BOOL                m_bTerminated;              ///< is Terminated
    HANDLE              m_DMAEvent;                 ///< DMA Event

    BOOL                m_bDMAEnable;               ///< Is DMA enabled

    /// IST Thread
    DWORD ThreadRun()
    {
        return 0;
    };
    
    
    int         m_Counter;                          ///< Transfer counter. Debug purpose
    int         m_LastTransfer;                     ///< Last Transfer Size. Debug purpose

    /// Complete Transfer.
    /// @param dwError      [IN] Error Code
    /// @return  Have transferred data structure
    PSTransfer CompleteTransfer(DWORD dwError);

    /// Send Fake Feature to MDD
    /// @param  bRequest        [IN] Request number
    /// @param  wFeatureSelector    [IN] Feature Selector
    /// @remarks UDC hardware can handle some request automatically. I need Fake request to send to MDD. 
    void SendFakeFeature(BYTE bReuqest,WORD wFeatureSelector);

    /// Send Data to FIFO
    /// @param  pBuffer         [IN] Data buffer
    /// @param  dwLength        [IN] Data lenght
    /// @return  length have transferred actually
    DWORD XmitData(PBYTE pBuffer, DWORD dwLength);

    /// Get Data From  FIFO
    /// @param  pBuffer         [IN] Data buffer
    /// @param  dwLength        [IN] Data lenght
    /// @return  length have transferred actually
    DWORD ReceiveData(PBYTE pBuffer, DWORD dwLength);    
};

#define EP0_MAX_PACKET_SIZE 0x10 

/// Endpoint Zero class
class PXAEndpointZero : public PXAEndpoint {
public:
    PXAEndpointZero(PXAUsbDevice * const pUsbDevice, BOOL bDoubleBuffer = TRUE ,BOOL bDMAEnable=FALSE) 
        : PXAEndpoint(pUsbDevice, 0 ,bDoubleBuffer) 
    {
        m_bNeedAck = FALSE;
        m_bSetupDirIn = FALSE;
        m_fInIST = FALSE;
    }
    BOOL Init(PUSB_ENDPOINT_DESCRIPTOR pEndpointDesc,
            BYTE bConfigurationValue, BYTE bInterfaceNumber, BYTE bAlternateSetting);   
    virtual BOOL ReInit();
    DWORD ResetEndpoint();
    DWORD IssueTransfer(PSTransfer pTransfer );
    DWORD SendControlStatusHandshake() ;
    DWORD   IST(DWORD dwIRBit);
protected:
    /// Need send ACK (zero package ) to host
    BOOL  m_bNeedAck;       
    BOOL  m_bSetupDirIn;        ///< Transfer Dir (IN or OUT)
    BOOL  m_fInIST ;            ///<    Is In IST
private:
    USB_DEVICE_REQUEST m_backup_udr;    ///< Backup UDR
    USB_DEVICE_REQUEST m_cur_udr;       ///< Current UDR
    BOOL               m_fBackedudr;        ///< is force backeupdr
};


///IN Endpoint Class
class PXAEndpointIn : public PXAEndpoint  {
public:
    PXAEndpointIn(PXAUsbDevice * const pUsbDevice,DWORD dwEndpointIndex, BOOL bDoubleBuffer = TRUE, BOOL bDMAEnable=FALSE);

    DWORD   IST(DWORD dwIRBit) ;

    DWORD ThreadRun();
    DWORD AbortTransfer(PSTransfer pTransfer );

private:
    void StartDMA();
    /// IST When Use IO Mode
    DWORD   ISTIO(DWORD dwIRBit) ;
    /// IST When Use DMA Mode
    DWORD   ISTDMA(DWORD dwIRBit) ;
    
};

///Out Endpoint Class
class PXAEndpointOut : public PXAEndpoint  {
public:
    PXAEndpointOut(PXAUsbDevice * const pUsbDevice,DWORD dwEndpointIndex, BOOL bDoubleBuffer = TRUE, BOOL bDMAEnable=FALSE );
    DWORD   IST(DWORD dwIRBit) ;
    static void OutDmaIntHandler (void* userContext, XLLP_UINT32_T channelDcsr);
private:

    DWORD   ISTIO(DWORD dwIRBit) ;
    DWORD   ISTDMA(DWORD dwIRBit) ;
    
    void StartDMA();    
    DWORD ThreadRun();  
    DWORD AbortTransfer(PSTransfer pTransfer );

    /// Copy data from DMA buffer to user buffer
    /// @returns  Data size have been copyed.
    DWORD UpdateFromDMABuffer();

    /// Config DMA Descript Chain
    /// @param  PhyAddress  [IN] Phycial address
    /// @param  Size            [IN] buffer size
    void ConfigDescriptChain(XLLP_UINT32_T PhyAddress, DWORD Size);
};

///Monahans OTG client Port 2 with internal Transcevier 
/// @remarks  This class used as NO-OTG mode. 
class MonOTGClientP2Int : public PXAUsbDevice
{
public:
    MonOTGClientP2Int( LPCTSTR lpActivePath)
        :  PXAUsbDevice(lpActivePath)
    {
        //MonOTGClientP2Int::Start();
    }
    
    virtual DWORD Start() ;
    virtual DWORD Stop() ;
        
};

/// Create USB Function driver object.  Platform related code create finial object
/// @param  lpActivePath        [IN] Active Key for the interface driver
/// @return     USB Function PDD Driver object
PXAUsbDevice * CreatePXAUsbDevice(LPCTSTR lpActivePath);

#endif




